/*==========================================================================
 *
 *  Copyright (C) 1995, 1996 Microsoft Corporation. All Rights Reserved.
 *
 *  File: rmstats.cpp
 *
 ***************************************************************************/
#include "rmfull.h"

/*
 * GLOBAL VARIABLES
 */
extern D3DAppInfo* d3dapp;          /* Pointer to read only collection of DD and D3D
                                       objects maintained by D3DApp */
extern rmfullglobals myglobs;       /* collection of global variables */

static struct {
    HFONT hFont;
    SIZE szFrameRate;
    SIZE szInfo;
} statglobs;

 /************************************************************************
  Frame rate and info text
 ************************************************************************/

BOOL
WriteFrameRateBuffer(float fps, long tps)
{
    HRESULT LastError;
    HDC hdc;
    RECT rc;
    char buf1[30], buf2[30], buf[60];
    int len;

    if (!myglobs.lpFrameRateBuffer)
        return TRUE;
    if (fps > 0.0f)
        wsprintf(buf1, "%d.%02d fps   ",
                            (int)( fps * 100 ) / 100,
                            (int)( fps * 100 ) % 100);
    else
        buf1[0] = 0;
    if (tps > 0)
        wsprintf(buf2, "%ld tps   ", tps);
    else
        buf2[0] = 0;
    len = wsprintf(buf, "%s%s", buf1, buf2);
    if (!myglobs.lpFrameRateBuffer)
        return FALSE;
    LastError = myglobs.lpFrameRateBuffer->GetDC(&hdc);
    if (LastError != DD_OK) {
        /*
         *  This is not vital, don't report an error.
         */
        return TRUE;
    }
    SelectObject(hdc, statglobs.hFont);
    SetTextColor(hdc, RGB(255,255,0));
    SetBkColor(hdc, RGB(0,0,0));
    SetBkMode(hdc, OPAQUE);
    GetTextExtentPoint32(hdc, buf, len, &statglobs.szFrameRate);
    SetRect(&rc, 0, 0, statglobs.szFrameRate.cx, statglobs.szFrameRate.cy);
    ExtTextOut(hdc, 0, 0, ETO_OPAQUE, &rc, buf, len, NULL);
    myglobs.lpFrameRateBuffer->ReleaseDC(hdc);
    return TRUE;
}

BOOL
WriteInfoBuffer(void)
{
    HRESULT LastError;
    HDC hdc;
    RECT rc;
    char buf[40];
    int len;

    if (!myglobs.lpInfoBuffer)
        return TRUE;
    if (d3dapp->bFullscreen)
        len = wsprintf(buf, "%dx%dx%d (%s)", d3dapp->ThisMode.w, d3dapp->ThisMode.h, d3dapp->ThisMode.bpp,
                       (d3dapp->ThisDriver.Desc.dcmColorModel == D3DCOLOR_MONO) ? "MONO" : "RGB");
    else
        len = wsprintf(buf, "%dx%d (%s)", d3dapp->szClient.cx, d3dapp->szClient.cy, 
                       (d3dapp->ThisDriver.Desc.dcmColorModel == D3DCOLOR_MONO) ? "MONO" : "RGB");
    if (!myglobs.lpInfoBuffer)
        return FALSE;
    LastError = myglobs.lpInfoBuffer->GetDC(&hdc);
    if (LastError != DD_OK) {
        /*
         *  This is not vital, don't report an error.
         */
        return TRUE;
    }

    SelectObject(hdc, statglobs.hFont);
    SetTextColor(hdc, RGB(255,255,0));
    SetBkColor(hdc, RGB(0,0,0));
    SetBkMode(hdc, OPAQUE);
    GetTextExtentPoint32(hdc, buf, len, &statglobs.szInfo);
    SetRect(&rc, 0, 0, statglobs.szInfo.cx, statglobs.szInfo.cy);
    ExtTextOut(hdc, 0, 0, ETO_OPAQUE, &rc, buf, len, NULL);
    myglobs.lpInfoBuffer->ReleaseDC(hdc);
    return TRUE;
}


BOOL
InitFontAndTextBuffers(void)
{
    DDCOLORKEY          ddck;
    DDSURFACEDESC       ddsd;
    HDC hdc;
    HRESULT ddrval;
    char dummyinfo[] = "000x000x00 (MONO) 0000";
    char dummyfps[] = "000.00 fps 00000000.00 tps 0000.00 mppps";
   
    /*
     * Create the font.
     */
    RELEASE(myglobs.lpInfoBuffer);
    RELEASE(myglobs.lpFrameRateBuffer);
    if (statglobs.hFont != NULL) {
        DeleteObject(statglobs.hFont);
    }
    statglobs.hFont = CreateFont(
        d3dapp->szClient.cx <= 600 ? 12 : 24,
        0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE,
        ANSI_CHARSET,
        OUT_DEFAULT_PRECIS,
        CLIP_DEFAULT_PRECIS,
        DEFAULT_QUALITY,
        VARIABLE_PITCH,
        "Arial" );

    hdc = GetDC(NULL);
    SelectObject(hdc, statglobs.hFont);
    GetTextExtentPoint(hdc, dummyfps, strlen(dummyfps), &statglobs.szFrameRate);
    GetTextExtentPoint(hdc, dummyinfo, strlen(dummyinfo), &statglobs.szInfo);
    ReleaseDC(NULL, hdc);

    memset( &ddsd, 0, sizeof( ddsd ) );
    ddsd.dwSize = sizeof( ddsd );
    ddsd.dwFlags = DDSD_CAPS | DDSD_HEIGHT |DDSD_WIDTH;
    ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN;
    ddsd.dwHeight = statglobs.szFrameRate.cy;
    ddsd.dwWidth = statglobs.szFrameRate.cx;
    ddrval = D3DAppCreateSurface(&ddsd, &myglobs.lpFrameRateBuffer);
    if (ddrval != DD_OK) {
        Msg("Could not create frame rate buffer.\n%s", D3DAppErrorToString(ddrval));    
        goto exit_with_error;
    }
    memset(&ddck, 0, sizeof(ddck));
    myglobs.lpFrameRateBuffer->SetColorKey(DDCKEY_SRCBLT, &ddck);
    if (!WriteFrameRateBuffer(0.0f, 0)) {
        goto exit_with_error;
    }
    memset( &ddsd, 0, sizeof( ddsd ) );
    ddsd.dwSize = sizeof( ddsd );
    ddsd.dwFlags = DDSD_CAPS | DDSD_HEIGHT |DDSD_WIDTH;
    ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN;
    ddsd.dwHeight = statglobs.szInfo.cy;
    ddsd.dwWidth = statglobs.szInfo.cx;
    ddrval = D3DAppCreateSurface(&ddsd, &myglobs.lpInfoBuffer);
    if (ddrval != DD_OK) {
        Msg("Could not create info buffer.\n%s", D3DAppErrorToString(ddrval));
        goto exit_with_error;
    }
    memset(&ddck, 0, sizeof(ddck));
    myglobs.lpInfoBuffer->SetColorKey(DDCKEY_SRCBLT, &ddck);
    if (!WriteInfoBuffer())
        goto exit_with_error;
    return TRUE;
exit_with_error:
    RELEASE(myglobs.lpInfoBuffer);
    RELEASE(myglobs.lpFrameRateBuffer);
    if (statglobs.hFont != NULL) {
        DeleteObject(statglobs.hFont);
    }
    return FALSE;
}

/*************************************************************************
  Frame rate output.
 *************************************************************************/

#define INTERVAL 100

char StatTxt[100];
int StatTxtLen;
int count = 0;
int last_polygons = 0;
int this_frames = 0;
time_t last_time;
float fps;
long tps;

/*
 * ResetFrameRate
 * Initializes the frame rate counter.
 */
void
ResetFrameRate(void)
{
    last_time = clock();
    count = 0;
    last_polygons = 0;
    this_frames = 0;
    fps = 0.0f; tps = 0;
    StatTxt[0] = 0;
    StatTxtLen = 0;
}

BOOL
CalculateFrameRate()
{
    /*
     * Calculate the frame rate and get other stats.
     */
    count++;
    this_frames++;
    if (count == INTERVAL) {
        double t;
        int p, f;
        time_t this_time;

        count = 0;
        this_time = clock();
        t = (this_time - last_time) / (double)CLOCKS_PER_SEC;
        last_time = this_time;

        p = myglobs.dev->GetTrianglesDrawn() - last_polygons;
        last_polygons = myglobs.dev->GetTrianglesDrawn();

        f = this_frames;
        this_frames = 0;

        fps = (float)(f / t);
        tps = (long)(p / t);

        if (myglobs.bShowFrameRate) {
            if (!WriteFrameRateBuffer(fps, tps))
                return FALSE;
        }
    }
    return TRUE;
}

/*
 * DisplayFrameRate
 * Outputs frame rate info and screen mode to back buffer when appropriate.
 */
BOOL
DisplayFrameRate(int* count, LPD3DRECT lpExtents )
{
    RECT rc;
    int x, y;
    HRESULT ddrval = DD_OK;
    if (myglobs.bShowFrameRate && !myglobs.bSingleStepMode && statglobs.szFrameRate.cx > 0 && statglobs.szFrameRate.cy > 0 &&
        statglobs.szFrameRate.cx < d3dapp->szClient.cx && statglobs.szFrameRate.cy < d3dapp->szClient.cy) {
        SetRect(&rc, 0, 0, statglobs.szFrameRate.cx, statglobs.szFrameRate.cy);
        x = (int)(0.5 * (d3dapp->szClient.cx - statglobs.szFrameRate.cx) + 0.5);
        y = 0;
        if (myglobs.lpFrameRateBuffer)
            ddrval = d3dapp->lpBackBuffer->BltFast(x, y, myglobs.lpFrameRateBuffer, &rc,
                                                 DDBLTFAST_SRCCOLORKEY | DDBLTFAST_WAIT);
        if (ddrval != DD_OK) {
            /*
             * Blting the frame rate has failed. Since it is not vital, we
             * aren't going to report an error.  Check to see if the surfaces
             * have been lost and then return.
             */
            if (ddrval == DDERR_SURFACELOST) {
                d3dapp->lpBackBuffer->Restore();
                myglobs.lpFrameRateBuffer->Restore();
            }
            return TRUE;
        }
        SetRect((LPRECT)(lpExtents), x, y, statglobs.szFrameRate.cx + x, statglobs.szFrameRate.cy);
        ++(*count);
        ++lpExtents;
    }
    if (myglobs.bShowInfo && statglobs.szInfo.cx < d3dapp->szClient.cx && statglobs.szInfo.cy < d3dapp->szClient.cy) {
        SetRect(&rc, 0, 0, statglobs.szInfo.cx, statglobs.szInfo.cy);
        x = (int)(0.5 * (d3dapp->szClient.cx - statglobs.szInfo.cx) + 0.5);
        y = d3dapp->szClient.cy - statglobs.szInfo.cy;
        if (myglobs.lpInfoBuffer)
            ddrval = d3dapp->lpBackBuffer->BltFast(x,y, myglobs.lpInfoBuffer, &rc,
                                                 DDBLTFAST_SRCCOLORKEY | DDBLTFAST_WAIT);
        if (ddrval != DD_OK) {
            /*
             * Blting the info has failed. Since it is not vital, we
             * aren't going to report an error.  Check to see if the surfaces
             * have been lost and then return.
             */
            if (ddrval == DDERR_SURFACELOST) {
                d3dapp->lpBackBuffer->Restore();
                myglobs.lpInfoBuffer->Restore();
            }
            return TRUE;
        }
        SetRect((LPRECT)(lpExtents), x, y, x + statglobs.szInfo.cx, y + statglobs.szInfo.cy);
        ++(*count);
    }
    return TRUE;
}
